package top.zhangjianyong.tools.service.api.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import top.zhangjianyong.tools.entity.BillFile;
import top.zhangjianyong.tools.entity.WaBill;
import top.zhangjianyong.tools.factory.BillFileFactory;
import top.zhangjianyong.tools.bill.manager.BillFileResolverManager;
import top.zhangjianyong.tools.bill.manager.WaBillAnalyzerManager;
import top.zhangjianyong.tools.service.api.BillService;
import top.zhangjianyong.tools.service.mybatis.IBillFileService;
import top.zhangjianyong.tools.service.mybatis.IWaBillService;
import top.zhangjianyong.tools.utils.ApiResponse;
import top.zhangjianyong.tools.utils.FileUtil;
import top.zhangjianyong.tools.user.UserHolder;
import top.zhangjianyong.tools.vo.BillDate;
import top.zhangjianyong.tools.vo.DailyBill;
import top.zhangjianyong.tools.vo.MonthBill;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ： zhangjianyong
 * @date ： 2022/7/17 12:45 PM
 */
@Slf4j
@Service
public class BillServiceImpl implements BillService {

    @Value("${bill.file.path}")
    private String billFilePath;
    @Autowired
    private IBillFileService iBillFileService;
    @Autowired
    private BillFileResolverManager billFileResolverManager;
    @Autowired
    private IWaBillService iWaBillService;
    @Autowired
    private WaBillAnalyzerManager waBillAnalyzerManager;


    @Override
    public ApiResponse upload(String password, MultipartFile file) throws IOException {
        Long userId = UserHolder.userId();
        String dir = billFilePath + File.separator + userId;
        List<File> list = FileUtil.unZip(file, dir, password);
        if (list.size() != 1) {
            return ApiResponse.fail("文件数量错误！");
        }
        File csv = list.get(0);
        log.info("解压后文件:" + csv.getName());
        BillFile billFile = BillFileFactory.create(csv, billFilePath);
        iBillFileService.save(billFile);

        return ApiResponse.success();
    }

    @Override
    public ApiResponse readFile(Long billFileId) throws IOException {
        BillFile billFile = iBillFileService.getById(billFileId);
        File csv = new File(billFilePath + billFile.getPath());
        //获取文件编码
        String fileCharset = FileUtil.getFileCharset(csv);
        log.info("文件编码>>>{}", fileCharset);
        //解析文件
        List<String> billLines = IOUtils.readLines(new FileInputStream(csv), Charset.forName(fileCharset));
        log.info("文件内容开始>>>>>");
        billLines.forEach(log::info);
        log.info("文件内容结束>>>>>");

        //解析数据
        billFileResolverManager.resolve(billLines, billFile);

        //保存解析后数据
        iBillFileService.updateById(billFile);

        return ApiResponse.success(csv.getName());

    }

    @Override
    public ApiResponse dates() {

        List<BillFile> billFiles = iBillFileService.list(UserHolder.userId(), "解析完成");
        //分组
        Map<String, List<BillFile>> billFileMap = billFiles.stream()
                .collect(Collectors.groupingBy(BillFile::getPlatform));

        Map<String, List<BillDate>> billDateMap = new HashMap<>();
        billFileMap.forEach((k, v) -> {
            List<BillDate> billDates = v.stream().map(billFile ->
                            new BillDate(billFile.getPlatform(), billFile.getStartDate(), billFile.getEndDate()))
                    .collect(Collectors.toList());
            billDates = mergeBillDate(billDates);
            billDateMap.put(k, billDates);
        });

        return ApiResponse.success(billDateMap);
    }

    private List<BillDate> mergeBillDate(List<BillDate> billDates) {
        Stack<BillDate> billDateStack = new Stack<>();

        billDates.stream().filter(billDate -> billDate.getStart() != null && billDate.getEnd() != null)
                .sorted(Comparator.comparing(BillDate::getStart))
                .forEach(billDate -> {
                    if (billDateStack.isEmpty()) {
                        billDateStack.push(billDate);
                    } else {
                        BillDate pop = billDateStack.pop();
                        List<BillDate> mergeBillDate = mergeBillDate(pop, billDate);
                        mergeBillDate.forEach(billDateStack::push);
                    }
                });
        return new ArrayList<>(billDateStack);
    }

    private List<BillDate> mergeBillDate(BillDate pop, BillDate billDate) {
        List<BillDate> list = new ArrayList<>();
        String pp = pop.getPlatform();
        String bp = billDate.getPlatform();
        if (!pp.equals(bp)) throw new RuntimeException("合并账期的平台不一致");
        LocalDate ps = pop.getStart();
        LocalDate pe = pop.getEnd();
        LocalDate bs = billDate.getStart();
        LocalDate be = billDate.getEnd();

        if (ps.isAfter(pe) || bs.isAfter(be)) throw new RuntimeException("账期开始时间小于结束时间");

        if (bs.isBefore(ps)) {
            throw new RuntimeException("合并账期数组排序错误");
        } else if (!bs.isAfter(pe.plusDays(1))) {
            //重合需要合并
            list.add(new BillDate(pp, ps, be));
        } else {
            //不需要合并
            list.add(pop);
            list.add(billDate);
        }
        return list;
    }


    @Override
    public ApiResponse daily() {
        Long userId = UserHolder.userId();
        LocalDateTime start = iWaBillService.minDate(userId);
        LocalDateTime end = iWaBillService.maxDate(userId);
        if (start == null || end == null) return ApiResponse.fail(201, "未导入账单");

        return ApiResponse.success(daily(start, end));
    }

    public List<DailyBill> daily(LocalDateTime start, LocalDateTime end) {
        Long userId = UserHolder.userId();
        if (start == null || end == null) throw new RuntimeException("未导入账单");
        //获取所有的订单，状态不等于关闭
        List<WaBill> list = iWaBillService.listBill(userId, start, end);

        //初始化Map，一天一条数据
        Map<LocalDate, DailyBill> dailyBillMap = new HashMap<>();
        LocalDate tmp = start.toLocalDate();
        LocalDate e = end.toLocalDate();
        while (!tmp.isAfter(e)) {
            dailyBillMap.put(tmp, new DailyBill(tmp));
            tmp = tmp.plusDays(1);
        }

        //统计数据
        List<DailyBill> dailyBillList = waBillAnalyzerManager.dailyBill(dailyBillMap, list);

        return dailyBillList;
    }

    @Override
    public ApiResponse month() {
        Long userId = UserHolder.userId();
        LocalDateTime start = iWaBillService.minDate(userId);
        LocalDateTime end = iWaBillService.maxDate(userId);

        if (start == null || end == null) return ApiResponse.fail(201, "未导入账单");
        //获取所有的订单，状态不等于关闭
        List<WaBill> list = iWaBillService.listBill(userId, start, end);

        //初始化Map，一月一条数据
        Map<YearMonth, MonthBill> monthBillMap = new HashMap<>();
        YearMonth tmp = YearMonth.from(start);
        YearMonth e = YearMonth.from(end);
        while (!tmp.isAfter(e)) {
            monthBillMap.put(tmp, new MonthBill(tmp));
            tmp = tmp.plusMonths(1);
        }
        //统计数据
        List<MonthBill> dailyBillList = waBillAnalyzerManager.monthBill(monthBillMap, list);

        return ApiResponse.success(dailyBillList);
    }

    @Override
    public ApiResponse rank(LocalDate date, String platform, String inOrOut, int page, int size) {
        LocalDateTime start = date.atTime(LocalTime.MIN);
        LocalDateTime end = date.atTime(LocalTime.MAX);
        Page<WaBill> rank = iWaBillService.rank(start, end, UserHolder.userId(), platform, inOrOut, page, size);
        return ApiResponse.success(rank);
    }

    @Override
    public ApiResponse rank(YearMonth month, String platform, String inOrOut, int page, int size) {
        LocalDateTime start = month.atDay(1).atTime(LocalTime.MIN);
        LocalDateTime end = month.atEndOfMonth().atTime(LocalTime.MAX);
        Page<WaBill> rank = iWaBillService.rank(start, end, UserHolder.userId(), platform, inOrOut, page, size);

        return ApiResponse.success(rank);
    }

    /**
     * 重新加载数据
     *
     * @return
     */
    @Deprecated
    @Override
    public ApiResponse reload() {

        billFileResolverManager.reload();
        return ApiResponse.success();
    }

    @Override
    public ApiResponse list() {
        Long userId = UserHolder.userId();
        List<BillFile> list = iBillFileService.list(userId);
        return ApiResponse.success(list);
    }

    @Override
    public ApiResponse del(Long billFileId) {
        iBillFileService.del(billFileId);
        return ApiResponse.success();
    }

    public static void main(String[] args) {
        LocalDateTime start = YearMonth.now().atDay(1).atTime(LocalTime.MIN);
        LocalDateTime end = YearMonth.now().atEndOfMonth().atTime(LocalTime.MAX);
        System.out.println(start);
        System.out.println(end);
    }
}
